package controllers;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import models.Action;
import models.Component;
import models.EffortUnit;
import models.EffortUnitProject;
import models.Event;
import models.Log;
import models.Note;
import models.Notification;
import models.Project;
import models.Role;
import models.Task;
import models.User;
import models.UserRoleProject;
import models.UserStory;
import notifiers.Mails;
import play.data.validation.Required;
import play.libs.Mail;
import play.mvc.Controller;
import play.mvc.With;

@With(Secure.class)
public class Projects extends Controller {

	/*
	 * displays a list of all projects a user is member of
	 */
	public static void showMyProjects() {
		long uid = Long.parseLong(session.get("userId"));
		User user = User.find("byId", uid).first();
		List<Project> projects = UserRoleProject
				.find(
						"select distinct urp.project from UserRoleProject urp where urp.user=? and urp.project.status=true and urp.status=true",
						user).fetch();
		render(projects);
	}

	/**
	 * Shows all projects
	 * 
	 * @author Loai_Ghoraba
	 */
	public static void showAllProjects() {
		List<Project> projects = UserRoleProject.find(
				"select distinct p from Project p where p.status=true").fetch();
		render(projects);
	}

	/**
	 * displays a description of a selected project
	 */

	public static void showProjectDescription(long pid) {
		Project project = Project.find("byId", pid).first();
		render(project);
	}

	/**
	 * The home page of a specific project, it includes related information of a
	 * project, e.g. name, description also checks if a user has permissions to
	 * invite a guest or invite a user to add the corresponding links to this
	 * action and if a user is not a member of the project it displays a link to
	 * join the project
	 * 
	 * @param projectId
	 *            the project Id
	 * 
	 */
	public static void showProject(long projectId) {
		Project project = Project.findById(projectId);
		long uid = Long.parseLong(session.get("userId"));
		User user = User.findById(uid);

		Project p = UserRoleProject
				.find(
						"select urp.project from UserRoleProject urp where urp.user=? and urp.project=?",
						user, project).first();
		boolean isMember = true;
		if (p == null)
			isMember = false;
		render(project, isMember);
	}

/**
 * selects all available roles in a project
 * @author selkasrawy
 * @param pId
 * @param inviteGuest
 * @param inviteUser
 */
public static void selectRoleInProject(long pId, boolean inviteGuest, boolean inviteUser) {
	System.out.println(pId);	
	Project project = Project.findById(pId);
		
		List<Role> roles = UserRoleProject
				.find(
						"select distinct urp.role from UserRoleProject urp where urp.project=? and urp.role.isSystem = false and urp.role.title!='System Admin' and urp.role.title!='Project Admin' and urp.role.title!='Product Owner' and urp.role.title!='Scrum Master' and urp.role.title!='Project Member' and urp.role.title!='Registered User' and urp.role.title!='Guest' order by urp.role.title",
						project).fetch();
		Role scrumMaster = UserRoleProject
				.find(
						"select urp.role from UserRoleProject urp where urp.project =? and urp.role.title='Scrum Master' and urp.project.status=true",
						project).first();
		Role projectAdmin = UserRoleProject
				.find(
						"select urp.role from UserRoleProject urp where urp.project =? and urp.role.title='Project Admin' and urp.project.status=true",
						project).first();
		if (scrumMaster == null)
			roles.add((Role) Role.find("byTitle", "Scrum Master").first());
		if (projectAdmin == null)
			roles.add((Role) Role.find("byTitle", "Project Admin").first());
		if(inviteGuest)
			{	
				renderArgs.put("project", project);
				renderArgs.put("roles", roles);
				render("Users/inviteGuestToProject.html");
				return;
			}
		if(inviteUser)
			{
				List<String> usernames = User.find("select user.userName from User user").fetch();
				List<String> usersemails = User.find("select user.email from User user").fetch();
				Object [] tmp = usernames.toArray();
				Object [] tmp1 = usersemails.toArray();
				String  users ="";
				String emails="";
				for(int i=0; i<tmp.length; i++)
				{
					users+= tmp[i].toString()+" ";
				}
				for(int i=0; i<tmp1.length; i++)
				{
					emails+=tmp1[i].toString()+" ";
				}				
				renderArgs.put("project", project);
				renderArgs.put("roles", roles);
				renderArgs.put("users", users);
				renderArgs.put("emails", emails);
				render("Users/inviteUserToProject.html");
				return;
			}
		else
		render(roles, project);
	}
/**
 * add a project request on the system and  notify the project admin
 * @author selkasrawy
 * @param selectionSubmit
 * @param projectId
 */
public static void sendProjectJoinRequest(long selection, long projectId) {
		long uId = Long.parseLong(session.get("userId"));
		long pId = projectId;
		long rId = selection;
		Role role =null;
		User user=null;
		Project project =null;
		String msg ="";
		String confirmation="";
		
		user = User.findById(uId);
		project = Project.findById(pId);
		List<User> projectAdmin = UserRoleProject.find("select urp.user from UserRoleProject urp where urp.project =? and urp.role.title = 'Project Admin'", project).fetch();
		if(rId == 0)
		{
			role = Role.find("select role from Role role where role.title='Project Member'").first();
			confirmation = "Your request to join "+project.name+" has been sent successfully";
			msg = "User "+user.userName+" has requested to join the project "+project.name;
		}
		else
		{
			role = Role.findById(rId);
			if(role == null)
			{
				params.flash();
				flash.error("This role does not exist");
				selectRoleInProject(pId, false, false);
			}
			UserRoleProject urp = UserRoleProject.find("select urp from UserRoleProject urp where urp.project =? and urp.user =? and urp.role=?", project, user, role).first();
			if(urp!=null)
			{
				params.flash();
				flash.error("You have requested/joined this role before. You can't join it again");
				selectRoleInProject(pId, false, false);
			}
			else{
				confirmation = "Your request to join "+project.name+" with role "+role.title+" has been sent successfully";
				msg = "User "+user.userName+" has requested to join the project "+project.name +" as a "+role.title;
			}
		}
		
		UserRoleProject urp = new UserRoleProject(false, user, role, project);
		urp.save();
		params.flash();
		flash.success(confirmation);
		Notifications.notifyAllUsers(projectAdmin, "Project Join Request", msg);
		new Log(uId, pId, Project.class
				.getName(), msg, pId, "").save();
		render("Application/index.html");
	}

	/**
	 * Just to render a page that contains the forum needed to create new
	 * project
	 */
	public static void createProjectPage() {	
		render();
	}

	/**
	 * Creates a project and notifies the system admin about it to respond to
	 * the creation request. The project is not shown in the project list unless
	 * the system admin approves it
	 * 
	 * @param name
	 *            the name of the project
	 * @param defaultSprintDuration
	 *            the sprint duration
	 * @param description
	 *            the description of the project
	 * @param numSprints
	 *            number of sprints in this project
	 * 
	 */
	public static void createProject(@Required String name,
			@Required String description) {
		// If there is another project with the same new name
		if (nameExists(name) || validation.hasErrors()) {
			flash.error(validation.hasErrors() ? "Fill in the fields !"
					: "This name already exists");
			// Keep the errors messages
			params.flash();
			// Reload the page again
			createProjectPage();
		}
		Project project = new Project(name, description, false).save();
		// associate built in roles to project
		associateBuiltInRoles(project);
		// check for system admin and accordingly add it automatically
		// Get the userId from the session
		long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		Role systemAdmin = UserRoleProject
				.find(
						"select r.role from UserRoleProject r where r.user = ? and r.role.title = ?",
						user, "System Admin").first();
		String flashMsg = "Project " + name
				+ " created ! Waiting for system admin to approve, pray :D";
		if (systemAdmin != null)// He is the system admin, create automatically
		{
			project.status = true;
			flashMsg = "Project " + name + " created successfully ";
		}
		flash.success(flashMsg);
		project.save();

		// Make the user the project admin of this project
		makeProjectAdmin(project, user);
		// Notify the project admin;
		Mails.notify(user, "Project creation response", flashMsg,
				project.status ? "/projects/showProject?projectId="
						+ project.getEntityId() : "");
		new Notification(userId, "Project creation", flashMsg).save();
		new Log(userId, (Long) project.getEntityId(), Project.class.getName(),
				flashMsg, "").save();
		// Reload the home page again
		showMyProjects();
	}

	/**
	 * Just redirect to the edit info page with the project rendered
	 * 
	 * @param projectId
	 *            the id of the project to edit
	 */
	public static void editInfoPage(long projectId) {
		Security.checkAccess("Edit my project", projectId, Project.class
				.getName());
		Project project = Project.findById(projectId);
		if (project == null) {
			flash.error("This project doesn't exist");
			showMyProjects();
		}
		render(project);
	}

	/**
	 * Is called when a project is created to associate to the created project
	 * the built in roles from the system
	 * 
	 * @author ayasa2r
	 * @param project
	 *            the project that will associate the built in roles to
	 */
	private static void associateBuiltInRoles(Project project) {
		project.save();
		List<Role> rolesIsSystem = Role.find("byIsSystem", true).fetch();
		for (int i = 0; i < rolesIsSystem.size(); i++) {
			// since those roles are not supposed to be in the project and the
			// project admin is already being added with the user who created
			// the project
			if (!rolesIsSystem.get(i).title.equals("System Admin")
					&& !rolesIsSystem.get(i).title.equals("Registered User")
					&& !rolesIsSystem.get(i).title.equals("Project Admin")) {
				// the role that is going to be copied
				Role temp = rolesIsSystem.get(i);
				// the role that is going to be actually added into the db
				Role actual = new Role(rolesIsSystem.get(i).title, false);
				for (int j = 0; j < temp.actions.size(); j++)
					actual.actions.add(temp.actions.get(j));
				actual.save();
				UserRoleProject urp = new UserRoleProject(true, null, actual,
						project).save();
				// creating a log entry with this action
				new Log(Long.parseLong(session.get("userId")), (Long) urp
						.getEntityId(), UserRoleProject.class.getName(),
						session.get("userId") + " created the role "
								+ actual.title + " in the project "
								+ project.name + ".", (Long) project
								.getEntityId(), "");
			}
		}
	}

	/**
	 * Edits this project name, description and default sprint duration,and /or
	 * the number of sprints in the project
	 * 
	 * @param projectId
	 *            the project id
	 * @param name
	 *            the new name
	 * @param description
	 *            the new description
	 * @param defaultSprintDuration
	 *            the new default sprint duration
	 * @param numOfSprints
	 *            the number of sprints to set
	 */
	public static void editProjectInfo(long projectId, @Required String name,
			@Required String description) {
		Project project = Project.findById(projectId);
		if (project == null) {
			flash.error("This project doesn't exist");
			showMyProjects();
		}
		Security.checkAccess("Edit my project", projectId, Project.class
				.getName());
		String oldName = project.name;
		// If there is ANOTHER project with the same new name
		if (!name.equals(oldName) && nameExists(name) || validation.hasErrors()) {
			flash.error(validation.hasErrors() ? "Fill in the fields !"
					: "This name already exists");
			// Keep the errors messages
			validation.keep();
			// Reload the page again
			editInfoPage(projectId);
		}
		// Edit the info
		project.name = name;
		project.description = description;
		project.save();
		// Display success message
		flash.success("Project " + name + " info edited !");

		// Notify the project Admin
		User projectAdmin = UserRoleProject
				.find(
						"select usp.user from UserRoleProject usp where usp.project=? and usp.role.title= 'Project Admin' ",
						project).first();
		Mails.notify(projectAdmin, "Project info edited", "Project " + name
				+ " info edited ! new info :- \nName: " + name
				+ "\nDescription: " + description,
				"/projects/showProject?projectId=" + project.getEntityId());
		// Get all users in this project and notify them
		List<User> users = User.find(
				"select urp.user from UserRoleProject urp where urp.project=?",
				project).fetch();
		Notifications.notifyAllUsers(users, "Project info changed", "project "
				+ project.name + " info changed ; new info :\n project name :"
				+ project.name + "\n project description: "
				+ project.description);
		new Log((Long) projectAdmin.getEntityId(),
				(Long) project.getEntityId(), Project.class.getName(),
				"project " + project.name + " info changed", (Long) project
						.getEntityId(), "").save();

		// Go to that project page, and see the reflected changes
		showProject(projectId);
	}

	/**
	 * Just redirect to the edit configuration page with the project rendered
	 * 
	 * @param projectId
	 *            the id of the project to edit
	 */

	public static void editConfPage(long projectId) {
		Project project = Project.findById(projectId);
		if (!canEditConfiguration(project))
			forbidden("Access Denied, E7na sabbetna el Doctor :D");

		// Get the project related effort unit types
		List<EffortUnit> projectEffortUnits = EffortUnitProject
				.find(
						"SELECT DISTINCT EuP.unit FROM EffortUnitProject EuP WHERE EuP.project=?",
						project).fetch();
		// Get the currently used effort unit for project
		EffortUnit selectedEffortUnit = EffortUnitProject
				.find(
						"SELECT EuP.unit FROM EffortUnitProject EuP WHERE EuP.project=? AND EuP.isSelected=?",
						project, true).first();		
		// Get the default system effort unit types
		List<EffortUnit> sysEffortUnits = EffortUnit.find("byIsSystem", true)
				.fetch();		
		// Obtain the union of both project units and system units
		Set<EffortUnit> distinctEffortUnits = new HashSet<EffortUnit>();
		distinctEffortUnits.addAll(sysEffortUnits);
		distinctEffortUnits.addAll(projectEffortUnits);

		render(distinctEffortUnits, selectedEffortUnit, project);

	}


	/**
	 * Edits the configurations of the project
	 * 
	 * @param projectId
	 *            the project id
	 * @param defaultSprintDuration
	 *            the default sprint duration
	 * @param effortUnit
	 * 			  The selected/newly created effort estimation metric name      
	 * 
	 */
	public static void editConf(long projectId, int defaultSprintDuration, String effortUnit) {
		// Get the project
		Project project = Project.findById(projectId);
		if(effortUnit==null)
		System.out.println("No effort unit received");
		if(effortUnit=="")
			System.out.println("Empty unit received");
		if (!canEditConfiguration(project))
			forbidden("Access Denied, E7na sabbetna el Doctor :D");
		// ----------------------------------------
		// Each one add his respective method here, and in the method check
		// if he has access , with out forbidden..look at editDefaultSprint
		// Duration for example
		// ----------------------------------------
				
		editDefaultSprintDuration(project, defaultSprintDuration);
		// Set the effort metric
		setEffortUnit(effortUnit, projectId);
		flash.success("Project " + project.name + " configurations saved successfully");
		showProject(projectId);
	}
	
	/**
	 * Returns whether he can edit any of the project configurations
	 * 
	 * @author Loai_Ghoraba
	 * @param project
	 *            the project
	 * @return whether he can edit any configuration
	 */
	public static boolean canEditConfiguration(Project project) {
		// See if he can have permission to edit anything of the configuration

		boolean canEditSprintDuration = Security.checkPermission(
				"Edit project default sprint duration", project);
		boolean canEditEffortEstimationMetric = Security.checkPermission(
				"Set effort estimation metric", project);
		// list all other configurations booleans

		return canEditSprintDuration || canEditEffortEstimationMetric; 
	}

	/**
	 * Edit the default sprint duration of a project
	 * 
	 * @param project
	 *            the project to edit
	 * @param defaultSprintDuration
	 *            the new default sprint duration
	 */
	static void editDefaultSprintDuration(Project project,
			int defaultSprintDuration) {
		// Checks that he has permission
		boolean canEditSprintDuration = Security.checkPermission(
				"Edit project default sprint duration", project);
		if (!canEditSprintDuration)
			return;
		//
		if (defaultSprintDuration < 1) {
			flash.error("default sprint duration must be more than zero ");
			editConfPage((Long) project.getEntityId());
		}
		project.defaultSprintDuration = defaultSprintDuration;
		project.save();
	}

	/**
	 * Shows the projects that are waiting for the system admin to approve their
	 * creation, should be called only by the system admin
	 */
	public static void showRequestedProjects() {
		Security.checkAccess("Respond to project creation requset", -1, "none");
		// Find all requested projects, that is find
		// the projects whose status are false
		List<Project> projects = Project.find("byStatus", false).fetch();
		render(projects);

	}

	/**
	 * Makes this <code>user</code> the project admin of this
	 * <code>project</code>
	 * 
	 * @param project
	 *            The project that he will become admin on
	 * @param user
	 *            The user that will become admin
	 */
	private static void makeProjectAdmin(Project project, User user) {
		Role admin = Role.find("byTitle", "Project Admin").first();
		Role temp = new Role("Project Admin", false);
		if (temp != null)
			for (int i = 0; i < admin.actions.size(); i++)
				temp.actions.add(admin.actions.get(i));
		temp.save();
		UserRoleProject usp = new UserRoleProject(true, user, temp, project);
		usp.save();
	}

	/**
	 * method that get all actions and passes them to the page called createrole
	 * 
	 * @param projectId
	 *            id of the project that the role is created in
	 */
	public static void createRole(long projectId) {
		Security.checkAccess("Create Project Role", projectId, Project.class
				.getName());
		List<Action> allActions = Action.findAll();
		for(int i=0;i<allActions.size();i++)
		{
			if(allActions.get(i).name.equals("Create System Role")
				||allActions.get(i).name.equals("Edit System Role")
				||allActions.get(i).name.equals("Delete System Role"))
				{
					allActions.remove(i);
					i--;
				}
		}
		List<Role> rList = UserRoleProject
				.find(
						"select distinct urp.role from UserRoleProject urp where urp.project=? and urp.role.title !=? and urp.role.title !=?",
						Project.findById(projectId), "Project Admin",
						"Project Member").fetch();

		render(allActions, projectId, rList);

	}

	/**
	 * method that is called when done is pressed in create role page
	 * 
	 * @param projectId
	 *            the project id of the project that the role will be added to
	 * @param Name
	 *            name of the role created
	 * @param actionId
	 *            array of Id's of actions checked
	 */
	public static void doneCreatingRole(long projectId, String name,
			 long roleId,long[] action) {
		long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		Project project = Project.findById(projectId);

		Role role = new Role(name, false);
		// created a role with the name that was entered
		role.save();
		UserRoleProject usp = new UserRoleProject(true, role, project);
		usp.project = project;
		// project.user_project_roles.add(usp);
		// project.save();
		usp.save();
		// user is entered as NULL
		List<Action> actions = new LinkedList<Action>();
		for (int i = 0; i < action.length; i++) {
			actions.add((Action) Action.findById(action[i]));
		}
		// for loop to add the actions that were selected to the list
		role.actions = actions;
		role.save();
		String desc = user.userName + " has created a new role in project "
				+ project.name;

		Log log = new Log(userId, role.getId(), "Role", desc, projectId, null);
		log.save();
		flash.success("Role created successfully");
		getAllRoles(projectId);
		// render("Projects/Roles.html", roles);
	}

	/**
	 * Called whenever a project name is set or changed to make sure there isn't
	 * another project with the same name
	 * 
	 * @param name
	 *            the new name of the project
	 * @return whether there is another project with the same name
	 */
	private static boolean nameExists(String name) {
		List<Project> projects = Project.findAll();
		for (int i = 0; i < projects.size(); i++) {
			// Get each project name
			String oldName = projects.get(i).name;
			// Check if it is equal to the new name
			if (name.equals(oldName))
				return true; // The name exist ! inform
			// the project admin
		}
		// This name is totally new. It is safe to use it
		return false;
	}

	/**
	 * Deletes a specific project
	 * 
	 * @param projectId
	 *            the required project to be deleted
	 * @author Adam El Shafei
	 */
	public static void deleteProject(long projectId) {
		Security.checkAccess("Delete my project", projectId, Project.class
				.getName());
		Project project = Project.findById(projectId);
		Calendar now = Calendar.getInstance();
		List<Event> temp = project.events;
		List<Event> events = new LinkedList<Event>();
		events.addAll(temp);
		Event defEvent = Events.getDefaultEvent(project);
		events.remove(defEvent);
		for (Event event : events)
			if (event.endDate.after(now)) {
				flash
						.error("cannot delete because there are events in progress..delete them first");
				showMyProjects();
			}
		project.delete();
		flash.success("Project " + project.name + " is deleted successfully");
		// redirect("http://localhost:9000/projects/showmyprojects");
		showMyProjects();
		// Notify the project Admin
		Role admin = Role.find("byTitle", "Project Admin").first();
		User projectAdmin = UserRoleProject
				.find(
						"select usp.user from UserRoleProject usp where usp.project=? and usp.role= ?",
						project, admin).first();
		Mail.send("coolSoft@SMT.com", projectAdmin.email, "Project deleted",
				"Project " + project.name + " deletion successful");
		// Get all users in this project and notify them
		List<User> users = User.find(
				"select urp.user from UserRoleProject urp where urp.project=?",
				project).fetch();
		Notifications.notifyAllUsers(users, "Project deleted", "Project "
				+ project.name + " has been deleted");
		new Log((Long) projectAdmin.getEntityId(),
				(Long) project.getEntityId(), Project.class.getName(),
				"project " + project.name + " has been deleted", (Long) project
						.getEntityId(), "").save();
	}

	/**
	 * Returns all current and up coming sprints in this project
	 * 
	 * @param project
	 *            the project to get sprints from
	 * @return all current and up coming sprints in this project
	 */
	private static List<Event> getPresentSprints(Project project) {
		// Get all sprints
		List<Event> allSprints = project.events;
		// Initialize the result list
		List<Event> presentSprints = new LinkedList<Event>();
		// Get current data
		Calendar currentDate = Calendar.getInstance();
		// Iterate through all sprints
		for (Event sprint : allSprints) {
			// If this sprint end date is after the current data, add it to the
			// list
			if (sprint.endDate.after(currentDate))
				presentSprints.add(sprint);
		}

		return presentSprints;
	}

	/**
	 * redirects to the createMeeting page with all projects in the database to
	 * choose from them
	 * 
	 * 
	 * @return void in case there is no project in the database
	 */
	public static void getProject(long id) {
		Project project = Project.findById(id);
		render("Events/createMeeting.html", project);
	}

	/**
	 * method that return all the roles in a specific project
	 * 
	 * @param projectId
	 *            id of project
	 * @return List<Role>
	 */
	public static void getAllRoles(long projectId) {

		Project project = Project.findById(projectId);
		List<Role> roles = UserRoleProject
				.find(
						"select distinct urp.role from UserRoleProject urp where urp.project=?",
						project).fetch();
		for (int i = 0; i < roles.size(); i++)
			if (roles.get(i).title.equals("Project Admin"))
			{
				roles.remove(i);
				break;
			}
		render("Projects/Roles.html", roles, projectId, project);
	}

	/**
	 * method that shows the first page of the editing of the role
	 * 
	 * @param roleId
	 *            is the role id of this role
	 * @param projectId
	 *            is the project id of this project
	 */
	public static void showEditRole(String title, long roleId, long projectId,
			long inheritRole) {
		// getting the role that needs to be edited
		Role r = Role.findById(inheritRole);
		// checking if being hacked
		if (r == null)
			forbidden("You are a Hacker!");
		Project project = Project.findById(projectId);
		List<Role> rList = UserRoleProject
				.find(
						"select distinct urp.role from UserRoleProject urp where urp.project=? ",
						project).fetch();
		List<Action> aSelList = r.actions;
		Role temp;
		temp = Role.find("byIsSystemAndTitle", true, "Project Admin").first();
		List<Action> aList = temp != null ? temp.actions
				: new LinkedList<Action>();
		boolean inPro;
		for (int i = 0; i < aSelList.size(); i++) {
			inPro = false;
			for (int j = 0; j < aList.size(); j++) {
				if (aSelList.get(i).name.equals(aList.get(j).name)) {
					inPro = true;
					break;
				}
			}
			if (!inPro)
				aSelList.remove(i);
		}
		boolean isSystemRole = false;
		List<Role> systemRoles = Role.find("byIsSystem", true).fetch();
		for (int i = 0; i < systemRoles.size(); i++)
			if (systemRoles.get(i).title.equals(title)) {
				isSystemRole = true;
				break;
			}
		// rendering the page accordingly
		render(title, roleId, rList, aList, aSelList, projectId, isSystemRole,
				r);
	}

	/**
	 * method to show the inherit page which is the same as the edit page but
	 * modified with the chosen role to inherit from
	 * 
	 * @param title
	 *            is the title of the role
	 * @param roleId
	 *            is the id of the role itself
	 * @param inheritRole
	 *            is the id of the inherited role
	 */
	public static void inheritEditRole(String title, long roleId,
			long projectId, long inheritRole) {
		// getting the role that needs to be edited
		Role r = Role.findById(inheritRole);
		// checking if being hacked
		if (r == null)
			forbidden("You are a Hacker!");
		Project project = Project.findById(projectId);
		List<Role> rList = UserRoleProject
				.find(
						"select distinct urp.role from UserRoleProject urp where urp.project=? ",
						project).fetch();
		List<Action> aSelList = r.actions;
		Role temp;
		temp = Role.find("byIsSystemAndTitle", true, "Project Admin").first();
		List<Action> aList = temp != null ? temp.actions
				: new LinkedList<Action>();
		boolean inPro;
		for (int i = 0; i < aSelList.size(); i++) {
			inPro = false;
			for (int j = 0; j < aList.size(); j++) {
				if (aSelList.get(i).name.equals(aList.get(j).name)) {
					inPro = true;
					break;
				}
			}
			if (!inPro)
				aSelList.remove(i);
		}
		boolean isSystemRole = false;
		List<Role> systemRoles = Role.find("byIsSystem", true).fetch();
		for (int i = 0; i < systemRoles.size(); i++)
			if (systemRoles.get(i).title.equals(title)) {
				isSystemRole = true;
				break;
			}
		// rendering the page accordingly
		render(title, roleId, rList, r, inheritRole, aList, aSelList,
				projectId, isSystemRole);
	}

	/**
	 * method to show the inherit page which is the same as the create page but
	 * modified with the chosen role to inherit from
	 * 
	 * @param title
	 *            is the title of the role
	 * @param roleId
	 *            is the id of the role itself
	 * @param inheritRole
	 *            is the id of the inherited role
	 */
	public static void inheritCreateRole(String title, long roleId,
			long inheritRole, long projectId) {
		// getting the role that needs to be edited
		Role r = Role.findById(inheritRole);
		// checking if being hacked
		if (r == null)
			forbidden("You are a Hacker!");
		// changing the lists according to the inheritance
		List<Role> rList = UserRoleProject
				.find(
						"select distinct urp.role from UserRoleProject urp where urp.project=? ",
						Project.findById(projectId)).fetch();
		Role temp = Role
				.find(
						"select urp.role from UserRoleProject urp where urp.role.title=?",
						"Project Admin").first();
		List<Action> aList;
		if (temp == null)
			aList = r.actions;
		else
			aList = temp.actions;
		List<Action> aSelList = r.actions;
		// rendering the page accordingly
		render(title, roleId, rList, r, aList, aSelList, projectId);

	}

	/**
	 * method to show that the role is edited within this project successfully
	 * 
	 * @param title
	 *            is the title of the role
	 * @param roleId
	 *            is the role id
	 * @param action
	 *            is the list of new actions belonging to this role
	 */
	public static void editRoleInProject(String title, long roleId,
			long[] action, long projectId) {
		// getting the role that needs to be edited
		Role r = Role.findById(roleId);
		Project project = Project.findById(projectId);
		List<Role> projectRoles = UserRoleProject.find(
				"select urp.role from UserRoleProject urp where urp.project=?",
				project).fetch();
		int count = 0;
		for (int i = 0; i < projectRoles.size(); i++)
			if (projectRoles.get(i).title != null)
				if (projectRoles.get(i).title.equals(title))
					count++;
		if (count > 1) {
			flash.error("The name " + title
					+ " already exists in the Roles in this project.");
			showEditRole(title, roleId, projectId, roleId);
		}
		// checking if being hacked
		if (r == null)
			forbidden("You are a Hacker!");
		// editing the role
		r.title = title;
		r.isSystem = false;
		List<Action> editActions = new LinkedList<Action>();
		for (int i = 0; i < action.length; i++) {
			editActions.add((Action) Action.findById(action[i]));
		}

		r.actions = editActions;
		// saving the edited role
		r.save();
		// rendering the page accordingly
		flash.success("The role " + title + " is edited successfully!");
		new Log(Long.parseLong(session.get("userId")), projectId, Project.class
				.getName(), session.get("userId")
				+ " edited the role "
				+ title
				+ " in the project "
				+ Project.find("select p.name from Project p where p=?",
						Project.findById(projectId)).first() + ".", "");
		User proAd = UserRoleProject
				.find(
						"select urp.user from UserRoleProject urp where urp.project=? and urp.role.title=?",
						project, "Project Admin").first();
		if (proAd != null)
			new Notification((Long) proAd.getEntityId(), "ROLE EDITED!",
					"A role is edited in your project roles!");
		getAllRoles(projectId);
	}

	/**
	 * Returns all the stories with a certain status in a project
	 * 
	 * @param projectId
	 *            the project id
	 * @return List<UserStory> a list of user stories in this project with this
	 *         status
	 */

	public static List<UserStory> getProjectStories(long projectId,
			String status) {
		Project project = Project.findById(projectId);
		List<UserStory> stories = new ArrayList<UserStory>();
		List<Component> comps = new ArrayList<Component>();
		comps.addAll(project.components);
		for (int i = 0; i < comps.size(); i++) {
			List<UserStory> tmpStories = new ArrayList<UserStory>();
			tmpStories.addAll(comps.get(i).userStoriesAssigned);
			for (int j = 0; j < tmpStories.size(); j++)
				// if (tmpStories.get(j).status.equals(status))
				stories.add(tmpStories.get(j));
		}
		return stories;
	}

	/**
	 * Returns the list of user stories in this project
	 * 
	 * @author Loai_Ghoraba
	 * @param project
	 *            the project
	 * @return the list of user stories in this project
	 * 
	 */
	public static List<UserStory> getAllProjectStories(Project project) {
		Set<UserStory> stories = new HashSet<UserStory>();
		for (Component comp : project.components)
			stories.addAll(comp.userStoriesAssigned);
		List<UserStory> res = new LinkedList<UserStory>();
		res.addAll(stories);
		return res;
	}

	/**
	 * Returns all the tasks with a certain status in a project
	 * 
	 * @param projectId
	 *            the project id
	 * @return List<Task> a list of tasks in this project with this status
	 */

	public static List<Task> getProjectTasks(long projectId, String status) {
		Project project = Project.findById(projectId);
		List<Event> events = new ArrayList<Event>();
		events.addAll(project.events);
		List<Task> tasks = new ArrayList<Task>();
		for (int i = 0; i < events.size(); i++) {
			List<Task> tmpTasks = new ArrayList<Task>();
			tmpTasks.addAll(events.get(i).tasks);
			for (int j = 0; j < tmpTasks.size(); j++) {
				if (!tasks.contains(tmpTasks.get(j)))
					if (tmpTasks.get(j).status.status.equals(status))
						tasks.add(tmpTasks.get(j));
			}
		}
		return tasks;
	}

	/**
	 * this method is for deleting a role within a project i get the project and
	 * the role then i get the list of users with this role i associate them
	 * with project member instead and delete this role
	 * 
	 * @param roleId
	 * @param projectId
	 */
	public static void deleteRole(Long roleId, Long projectId) {
		Security.checkAccess("Delete Project Role", projectId, Project.class
				.getName());
		Project project = Project.findById(projectId);
		List<UserRoleProject> urp = UserRoleProject
				.find(
						"select urp from UserRoleProject urp where urp.project=? and urp.role=?",
						project, Role.findById(roleId)).fetch();
		List<User> u = UserRoleProject
		.find(
				"select urp.user from UserRoleProject urp where urp.project=? and urp.role=?",
				project, Role.findById(roleId)).fetch();
		List<Role> r = UserRoleProject
		.find(
				"select urp.role from UserRoleProject urp where urp.project=? ",
				project).fetch();
		Role member = null;
		for(int i=0;i<r.size();i++)
		{
			if(r.get(i).title.equals("Project Member"));
			{
				member=r.get(i);
				break;
			}
		}
		for (int i = 0; i < urp.size(); i++) {
			if (urp.get(i).user == null && urp.get(i).role.isSystem == false) {

				Role y = urp.get(i).role;
				y.delete();
				urp.remove(i);
				project.user_project_roles.remove(urp);
			} else {
				urp.get(i).role = member;
				urp.get(i).user = urp.get(i).user;
				urp.get(i).save();
			}
		}
		long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);

		String desc = user.userName + " has deleted a role in project "
				+ project.name;

		Log log = new Log(userId, roleId, "Role", desc, projectId, null);
		log.save();
		Notifications.notifyAllUsers(u, "Role deleted", "You role has been deleted from the project,You are a project member now");
		getAllRoles(projectId);
	}

	/**
	 * Displays the list of effort metrics available for an arbitrary project to
	 * select one of them or create and select a new one
	 * 
	 * @param projectId
	 *            The id of the project to display the list of metrics for
	 */
	public static void changeEffortUnit(long projectId) {
		Project currentProject = Project.findById(projectId);
		List<EffortUnit> projectEffortUnits = EffortUnitProject
				.find(
						"SELECT DISTINCT EuP.unit FROM EffortUnitProject EuP WHERE EuP.project=?",
						currentProject).fetch();
		List<EffortUnit> sysEffortUnits = EffortUnit.find("byIsSystem", true)
				.fetch();
		Set<EffortUnit> distinctEffortUnits = new HashSet<EffortUnit>();
		distinctEffortUnits.addAll(sysEffortUnits);
		distinctEffortUnits.addAll(projectEffortUnits);
		render(distinctEffortUnits, currentProject);
	}


	/**
	 * Sets the effort metric for an arbitrary project to an existing metric or
	 * creates a new one and set it
	 * 
	 * @param effortUnit
	 *            The name of the selected existing/new effort metric to set
	 * @param projId
	 *            The id of the project for which the effort metric is changed
	 */
	static void setEffortUnit(String effortUnit, long projId) {
		Project currentProject = Project.findById(projId); 
		boolean canSetEffortUnit = Security.checkPermission(
				"Set effort estimation metric", currentProject);
		if (!canSetEffortUnit)
			return;
		//Find the project instance
		
		if (effortUnit == null || effortUnit == "" || currentProject == null)
			flash.error("Invalid arguments supplied");
		// Search for an existing effort unit with the supplied name
		EffortUnit selectedEffortUnit = EffortUnit.find("byUnit", effortUnit)
				.first();
		// Create a new one if unit does not exist
		if (selectedEffortUnit == null)
			selectedEffortUnit = new EffortUnit(false, effortUnit).save();
		// Search for an existing relation between the unit and the current
		// project
		EffortUnitProject eup = EffortUnitProject.find("byProjectAndUnit",
				currentProject, selectedEffortUnit).first();
		// Create a new relation if none exist
		if (eup == null)
			eup = new EffortUnitProject(true, selectedEffortUnit,
					currentProject).save();

		// Get all EffortUnitProject relations related to the current project
		// and deselect them
		List<EffortUnitProject> projectEUs = EffortUnitProject.find(
				"byProjectAndIsSelected", currentProject, true).fetch();

		for (EffortUnitProject anEUP : projectEUs)
			if (anEUP != eup) {
				anEUP.isSelected = false;
				anEUP.save();
			}
	}

	/**
	 * The page to create note in a project
	 * 
	 * @param projectId
	 *            the project id
	 */
	public static void createNotePage(long projectId) {
		Project project = Project.findById(projectId);
		List<Event> projectEvents = Event
				.find(
						"select e from Event e where e.project=? and e.sprint is null and e.type.type!='Default' and e.type.type!='Sprint'",
						project).fetch();
		List<Event> sprintsEvents = Event.find(
				"select e from Event e where e.sprint.project=?", project)
				.fetch();
		List<Event> events = new LinkedList<Event>();
		events.addAll(projectEvents);
		events.addAll(sprintsEvents);
		render(project, events);

	}

	/**
	 * Creates a note and associate it to the list of events passed, if none is
	 * passed, it associates it to the default event
	 * 
	 * @param projectId
	 *            the id of the project
	 * @param description
	 *            the note text
	 * @param eventsIds
	 *            the list of events
	 * @param name
	 *            the note name
	 * 
	 */
	public static void createNote(long projectId, String name,
			String description, long[] eventsIds) {
		Project project = Project.findById(projectId);
		List<Event> events = getProjectEvents(project);
		events.add(Events.getDefaultEvent(project));
		List<Note> oldNotes = new LinkedList<Note>();
		for (Event event : events)
			oldNotes.addAll(event.notes);
		for (Note note : oldNotes)
			if (note.name != null && note.name.equals(name)) {
				flash.error("This name already exists !! chose another one");
				params.flash();
				createNotePage(projectId);
			}
		Note note = new Note(name, description);
		note.save();
		if (eventsIds == null) {
			Event defaultEvent = Events.getDefaultEvent(project);
			defaultEvent.notes.add(note);
			defaultEvent.save();
		} else {
			List<Event> allEvents = Events.getAllProjectEvents(project);
			for (long id : eventsIds) {
				Event event = Event.findById(id);
				// If he hack
				if (!allEvents.contains(event)) {
					note.delete();
					forbidden();
				}
				event.notes.add(note);
				event.save();
			}
		}
		flash.success("Note created successfully");
		showProject(projectId);
	}

	/**
	 * Calls render to show the form required to create a Component, which in
	 * turn calls createComponent
	 * 
	 * @param projectId
	 *            the ID of the project to which this sprint is added
	 * 
	 */
	public static void createComponentForm(long projectId) {
		Project project = Project.findById(projectId);
		render(project);
	}

	/**
	 * Creates a Component
	 * 
	 * @param projectId
	 *            the project with which to associate
	 * @param name
	 *            the Component name
	 * @param description
	 *            the Component description/goal
	 * 
	 */
	public static void createComponent(long projectId, String name,
			String description) {
		if (name.trim().isEmpty()) {
			flash.error("Component name can not be empty");
			createComponentForm(projectId);
		}
		if (name.equals("Default component")) {
			flash.error("Component name can not be 'Default component'");
			createComponentForm(projectId);
		}
		if (description.trim().isEmpty()) {
			flash.error("Component description can not be empty");
			createComponentForm(projectId);
		}
		Project project = Project.find("byId", projectId).first();
		Component component = new Component(name, description, project);
		component.save();
		flash.success("Component created successfuly!");
		showProject(projectId);
	}

	/**
	 * returns a list of all events in the project
	 * 
	 * @param project
	 *            the project
	 * @return all events in the project
	 */
	public static List<Event> getProjectEvents(Project project) {
		List<Event> sprints = project.events;
		List<Event> events = new LinkedList<Event>();
		for (Event sprint : sprints)
			events.addAll(sprint.events);
		for (Event sprint : sprints)
			if (!sprint.type.type.equals("Sprint")
					&& !sprint.type.type.equals("Default"))
				events.add(sprint);
		return events;
	}
}